﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FinanSoft.Utilities;
using FinanSoft.Entities;
using System.Xml;
using FinanSoft.BusinessLayer;
using System.Data.SqlClient;

namespace FinanSoft.DataLayer
{
    class PeriodDataManager
    {

        #region Singleton

        private PeriodDataManager()
        {}
        /// <summary>
        /// Unica Instancia de esa clase
        /// </suammary>
        private static PeriodDataManager _instance;

        /// <summary>
        /// Metodo para implementar el patrón singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static PeriodDataManager GetInstance() 
        {
            if (_instance == null)
                _instance = new PeriodDataManager();
            return _instance;
        }
        #endregion 

        #region Métodos

        /// <summary>
        /// Obtiene todos los posibles estados de una temporada. Deberían ser solo los siguientes 3 siempre:
        /// Abierto, Abierto excepto ventas y Cerrado. En caso de que alguno de ellos esté faltando devuelve un arreglo de
        /// 0 elementos. Si falla la conexión con la base de datos o hay algún otro error devuelve nulo.
        /// </summary>
        /// <returns>Arreglo con los estados de las temporadas. Es de 3 o 0 elementos y también puede devolver nulo</returns>
        public SeasonStatus[] GetSeasonStatus()
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_GET_SEASON_STATUS;
                SqlParameter[] parameters = new SqlParameter[0];
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                if (result.HasRows)
                {
                    SeasonStatus[] status = new SeasonStatus[Constants.STATUS_AMOUNT];
                    for (int statusIndex = 0; statusIndex < status.Length; statusIndex++)
                    {
                        if (!result.Read())
                            return new SeasonStatus[0];
                        status[statusIndex] = new SeasonStatus(Convert.ToInt32(result[Constants.SP_COLUMN_GET_SEASON_STATUS_ID]), result[Constants.SP_COLUMN_GET_SEASON_STATUS_NAME].ToString(), "");
                    }
                    return status;
                }
                else
                    return new SeasonStatus[0];
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// Agrega un periodo a la base de datos
        /// </summary>
        /// <param name="newPeriod">Periodo con idTipo, Nombre, FechaInicio, FechaFin</param>
        /// <returns>retorna exito si se introdujo a la base de datos</returns>
        
        public bool CreateNewPeriod(AccountingPeriod periodo)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_ADD_PERIOD;
                //@Nombre VARCHAR(50), @NombreExtranjero VARCHAR(50), @EsTitulo BIT, @IdMoneda SMALLINT, @IdCuentaPadre INT, @Codigo INT
                SqlParameter[] parameters = new SqlParameter[4];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_ADD_PERIOD_ID_TIPO, periodo.Type);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_ADD_PERIOD_NOMBRE, periodo.Name);
                parameters[2] = new SqlParameter(Constants.SP_PARAM_ADD_PERIOD_FECHA_INICIO, periodo.StartDate);
                parameters[3] = new SqlParameter(Constants.SP_PARAM_ADD_PERIOD_FECHA_FIN, periodo.EndDate);
                Console.WriteLine(parameters[0]);
                Console.WriteLine(Constants.SP_PARAM_ADD_PERIOD_NOMBRE + " *** " + periodo.Name);
                Console.WriteLine(Constants.SP_PARAM_ADD_PERIOD_FECHA_INICIO + " *** " + periodo.StartDate);
                Console.WriteLine(Constants.SP_PARAM_ADD_PERIOD_FECHA_FIN + " *** " + periodo.EndDate);
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));

                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
               
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }




        /// <summary>
        /// Método que se encarga de leer el catálogo contable de la base de datos y mandar
        /// a parsear el xml que el stored procedure devuelve para convertirlo en un objeto
        /// de catalogo contable
        /// </summary>
        /// <returns>Retorna el objeto del catálogo contable</returns>
        public AccountingPeriod[] GetAccountingPeriod()
        {
            
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_GET_PERIODS;
                SqlParameter[] parameters = new SqlParameter[0];
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                if (result.HasRows)
                {
                    result.Read();
                    string xmlString = result.GetString(0);
                    dataHelper.CloseReader();
                    xmlString = Constants.ROOT_NODE_BEGINNING + xmlString + Constants.ROOT_NODE_ENDING;
                    
                    // Se crea un documento XML con el string recibido
                    XmlDocument xmlPeriod = new XmlDocument();
                    
                    xmlPeriod.LoadXml(xmlString);
                    return BuildPeriod(xmlPeriod);
                }
                else
                    return new AccountingPeriod[0];
            }
            catch (Exception ex)
            {
                return null;
            }

            //String xmlString = "<Cuenta IdCuenta='1' Nombre='1. Activos' NombreExtranjero='Activos' EsTitulo='1' IdMoneda='2' Tipo='Activos' Codigo='100000'><Cuenta IdCuenta='21' Nombre='1-10. Activos circulantes' NombreExtranjero='Activos circulantes' EsTitulo='1' IdMoneda='2' Tipo='Activos' Codigo='110000'><Cuenta IdCuenta='24' Nombre='1-10-100 Bancos' NombreExtranjero='Bancos' EsTitulo='0' IdMoneda='2' Tipo='Activos' Codigo='111000' MontoLocal='2120000.0000' MontoSistema='4000.0000' /></Cuenta></Cuenta><Cuenta IdCuenta='10' Nombre='2. Pasivos' NombreExtranjero='Pasivos' EsTitulo='1' IdMoneda='2' Tipo='Pasivos' Codigo='200000'><Cuenta IdCuenta='25' Nombre='2-10. Deudas corto plazo' NombreExtranjero='Deudas corto plazo' EsTitulo='0' IdMoneda='2' Tipo='Pasivos' Codigo='210000' MontoLocal='2120000.0000' MontoSistema='4000.0000' /></Cuenta><Cuenta IdCuenta='12' Nombre='3. Patrimonio' NombreExtranjero='Patrimonio' EsTitulo='1' IdMoneda='2' Tipo='Capital' Codigo='300000' /><Cuenta IdCuenta='13' Nombre='4. Ingresos' NombreExtranjero='Ingresos' EsTitulo='1' IdMoneda='2' Tipo='Ingresos' Codigo='400000' /><Cuenta IdCuenta='14' Nombre='5. Costos' NombreExtranjero='Costos' EsTitulo='1' IdMoneda='2' Tipo='Gastos' Codigo='500000' /><Cuenta IdCuenta='15' Nombre='6. Gastos' NombreExtranjero='Gastos' EsTitulo='1' IdMoneda='2' Tipo='Gastos' Codigo='600000' /><Cuenta IdCuenta='19' Nombre='7. Otros ingresos' NombreExtranjero='Otros ingresos' EsTitulo='1' IdMoneda='2' Tipo='Ingresos' Codigo='700000' /><Cuenta IdCuenta='20' Nombre='8. Otros gastos' NombreExtranjero='Otros gastos' EsTitulo='1' IdMoneda='2' Tipo='Gastos' Codigo='800000' />";
        }


        

        /// <summary>
        /// Método que recibe el documento xml a parsear para convertirlo en un periodo
        /// </summary>
        /// <param name="xmlPeriod">es el string xml a parsear</param>
        /// <returns>retorna un arreglo de todos los periodos con las temporadas respectivas</returns>
        public AccountingPeriod[] BuildPeriod(XmlDocument xmlPeriod)
        {
            
            // Aquí llamo a la clase de la base de datos que me regresa el string
            //String xmlString = "";
            XmlNodeList nodeChildren = xmlPeriod.FirstChild.ChildNodes;
            
            AccountingPeriod[] arrayPeriods = new AccountingPeriod[nodeChildren.Count];
            

            // Por cada nodo principal
            int periodIndex = 0;
            
            foreach (XmlNode node in nodeChildren)
            {
                
                
                AccountingPeriod periodos = new AccountingPeriod();
                
                periodos.Id = System.Convert.ToInt32(node.Attributes.GetNamedItem(Constants.TAG_NAME_PERIOD_ID).Value);
                periodos.Name = node.Attributes.GetNamedItem(Constants.TAG_NAME_PERIOD_NOMBRE).Value;
                periodos.Type = System.Convert.ToInt32(node.Attributes.GetNamedItem(Constants.TAG_NAME_PERIOD_ID_TIPO).Value);
                periodos.StartDate = System.Convert.ToDateTime(node.Attributes.GetNamedItem(Constants.TAG_NAME_PERIOD_FECHA_INICIO).Value);
                periodos.EndDate = System.Convert.ToDateTime(node.Attributes.GetNamedItem(Constants.TAG_NAME_PERIOD_FECHA_FIN).Value);
                periodos.Seasons = buildSeason(node, periodos.Id);

                arrayPeriods[periodIndex] = periodos;
                periodIndex++;

            }
            
            return arrayPeriods;
        }

        /// <summary>
        /// Método que se encarga de crear las Temporadas de un Periodo apartir de un xml a parsear
        /// </summary>
        /// <param name="Nodo">es un XmlNode que es el periodo</param>
        /// <param name="periodo">el numero del periodo</param>
        /// <returns>un arreglo de todas las temporadas</returns>
        public Season[] buildSeason(XmlNode Nodo, int periodo) 
        {
            XmlNodeList hijosHijos = Nodo.ChildNodes;
            Season[] temporadas =  new Season[hijosHijos.Count];
           int seasonIndex = 0;
            foreach (XmlNode nodito in hijosHijos) 
            {
                Season temp = new Season();

                temp.Id = System.Convert.ToInt32(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_ID).Value);
                temp.Name = nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_NOMBRE).Value;
                temp.Period = periodo;
                temp.Status = System.Convert.ToInt32(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_STATUS).Value);
                temp.StartDate = System.Convert.ToDateTime(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_START_DATE).Value);
                temp.EndDate = System.Convert.ToDateTime(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_END_DATE).Value);
                temp.InitialDueDate = System.Convert.ToDateTime(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_INITIAL_DUE_DATE).Value);
                temp.EndDueDate = System.Convert.ToDateTime(nodito.Attributes.GetNamedItem(Constants.TAG_NAME_SEASON_END_DUE_DATE).Value);

                temporadas[seasonIndex] = temp;
                seasonIndex++;
            }
            return temporadas;
        }



        /// <summary>
        /// Metodo que se encarga de crear una nueva temporada
        /// </summary>
        /// <param name="Temporada">es un objeto Season que se incluye a la base de datos</param>
        /// <returns>retorna éxito si se introduce a la base de datos</returns>
        public bool CreateNewSeason(Season Temporada)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_NEW_SEASON;

                SqlParameter[] parameters = new SqlParameter[6];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_ID_PERIODO, Temporada.Period);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_NOMBRE, Temporada.Name);
                parameters[2] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_FECHA_INICIO, Temporada.StartDate);
                parameters[3] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_FECHA_FIN, Temporada.EndDate);
                parameters[4] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_FECHA_INICIO_VENCIMIENTO, Temporada.InitialDueDate);
                parameters[5] = new SqlParameter(Constants.SP_PARAM_ADD_SEASON_FECHA_FIN_VENCIMIENTO, Temporada.EndDueDate);
                
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));

                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }


        /// <summary>
        /// Funcion que se encarga de modificar el estado de una temporada
        /// </summary>
        /// <param name="Temporada">recibe un objeto para cambiar el estado de la temporada</param>
        /// <returns>retorna exito si es actualizado con exito</returns>
        public bool ModifyStatusSeason(Season Temporada)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_MOD_SEASON;
                //@Nombre VARCHAR(50), @NombreExtranjero VARCHAR(50), @EsTitulo BIT, @IdMoneda SMALLINT, @IdCuentaPadre INT, @Codigo INT
                SqlParameter[] parameters = new SqlParameter[2];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_MOD_ID_SEASON, Temporada.Id);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_MOD_ID_ESTADO, Temporada.Status);

                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));

                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        #endregion
    }
}
